package com.surrealbananapeel.battleship;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.surrealbananapeel.battleship.Point.State;
/**
 * This class is used for holding the statistics of all games. As well as the games 
 * being played.
 * 
 * @author Emmett Little and Alberto Saavedra
 */
public class Match
{
    private static Match instance;
    private ArrayList<Game> games;
    private Point statsOpponent[][][];
    private Point statsSBP[][][];
    
    private int numWinGames;
    private int numLoseGames;
    
    /**
     * Creates a new instance of the Match class. The constructor initialises a list of games, and
     * the statistics for all games.   
     */
    private Match()
    {
        this.games = new ArrayList<Game>();
        numWinGames = 0;
        numLoseGames = 0;
        statsOpponent = new Point[BattleshipClient.WIDTH][BattleshipClient.HEIGHT][BattleshipClient.DEPTH];
        statsSBP = new Point[BattleshipClient.WIDTH][BattleshipClient.HEIGHT][BattleshipClient.DEPTH];
        
        for (int i = 0; i < BattleshipClient.WIDTH; i++)
        {
            for (int j = 0; j < BattleshipClient.HEIGHT; j++)
            {
                for (int k = 0; k < BattleshipClient.DEPTH; k++)
                {
                    statsOpponent[i][j][k] = new Point(i, j, k);
                    statsSBP[i][j][k] = new Point(i, j, k);
                }
            }
        }
    }
    
    /**
     * This method ensures that only one instance of this class is created, and returned.
     * 
     * @return The unique instance of this class.
     */
    public static Match getInstance()
    {
        if (instance == null)
        {
            instance = new Match();
        }
        return instance;
    }
    
    /**
     * This method updates the  
     * 
     * @param point
     * @param shipName
     */
    public void updateCurrentGame(Point point, String shipName)
    {
        currentGame().updateBoard(point, shipName);
    }
    
    /**
     * Updates the statistics of the opponent by storing their las shot.
     * 
     * @param point The last point they made.
     * @param turn  An integer representing the turn of the players 
     *            0 opponent, 1 ours
     */
    public void updateStats(Point p, int turn)
    {
        
        if (turn == 0)
        {
            // Opponent
            ((Point) statsOpponent[p.getX()][p.getY()][p.getZ()])
                    .incrementTimesUsed();
        }
        else
        {
            // Our turn
            if (p.getState() == State.HIT || p.getState() == State.DETECTED)
            {
                ((Point) statsSBP[p.getX()][p.getY()][p.getZ()])
                        .incrementTimesUsed();
            }
            
        }
    }
    
    /**
     * This method adds a current game to the list of games at the first position.  
     */
    public void startNewGame()
    {
        this.games.add(0, new Game());
    }
    
    /**
     * This method gets the first game from the list of games. This game represents the
     * current game in this match. 
     * @return
     */
    public Game currentGame()
    {
        if (games.size() == 0)
        {
            return null;
        }
        return this.games.get(0);
    }
    
    /**
     * This method determines whether a shot in the current game has been made.
     * 
     * @param p The point that is going to be checked/
     * @return A Boolean True if the given shot has been made previously in the current game.
     */
    public boolean hasBeenShot(Point p)
    {
        Game game = currentGame();
        if (game == null)
        {
            return false;
        }
        return game.hasBeenShot(p);
    }
    
    /**
     * This method is a general method that returns the statistics of a given 
     * player.
     * 
     * @param turn  An integer representing the player's turn. 
     *              0 - for the opponent's stats
     *              1 - for our stats
     * @return the list containing the stats of the given player
     */
    public List<Point> getStats(int turn)
    {
        
        List<Point> points = new ArrayList<Point>();
        for (int i = 0; i < BattleshipClient.WIDTH; i++)
        {
            for (int j = 0; j < BattleshipClient.HEIGHT; j++)
            {
                for (int k = 0; k < BattleshipClient.DEPTH; k++)
                {
                    if (turn == 0)
                    {
                        if (((Point) statsOpponent[i][j][k]).getTimesUsed() > 2)
                        {
                            points.add((Point) statsOpponent[i][j][k]);
                        }
                    }
                    else if (turn == 1)
                    {
                        if (((Point) statsSBP[i][j][k]).getTimesUsed() > 1)
                        {
                            points.add((Point) statsSBP[i][j][k]);
                        }
                    }
                    
                }
            }
        }
        return points;
    }
    
    /*
     * This method returns a list with the opponent's game statistics. 
     * 
     * @return A list of points
     */
    public List<Point> getOppStats()
    {
        List<Point> points = getStats(0);
        Collections.sort(points, new CustomComparator());
        return points;
    }
    
    /**
     * This method returns a list with our game statistics.
     * 
     * @return A list of points 
     */
    public List<Point> getSBPStats()
    {
        List<Point> points = getStats(1);
        Collections.sort(points, new CustomComparator());
        return points;
    }
    
    /**
     * Custom comparator used to compare points based on their times used on the
     * board. 
     */
    public class CustomComparator implements Comparator<Point>
    {
        @Override
        public int compare(Point o1, Point o2)
        {
            return o2.getTimesUsed() - o1.getTimesUsed();
        }
    }
    
    /**
     * This method returns the number of games played.
     * 
     * @return An integer representing the number of games.
     */
    public int getNumberOfGames()
    {
        return games.size();
        
    }

    /**
     * This method returns the number of win games.
     * 
     * @return Number of win games.
     */
	public int getNumberOfWinGames() {
		return numWinGames;
	}
	/**
     * This method increments the number of win games.
     * 
     * @return Number of win games.
     */
	public void incrementWinGames() {
		this.numWinGames++;
	}
	
	  /**
     * This method returns the number of lose games.
     * 
     * @return Number of lose games.
     */
	public int getNumberOfLoseGames() {
		return numLoseGames;
	}
	/**
     * This method increments the number of lose games.
     * 
     * @return Number of win games.
     */
	public void incrementLoseGames() {
		this.numLoseGames++;
	}
    
}
